﻿using System;
using System.Globalization;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using DotNetNuke.Common;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Tabs;
using DotNetNuke.Security;

namespace HeroldIT.Dnn.Modules.WebControls
{
    /// <summary>
    /// A hierarchical DropDownList for selecting a specific module.
    /// </summary>
    public class ModuleSelector : UserControl
    {
        #region fields

        private HierarchicalDropDownList hddSelector;

        #endregion

        #region properties

        /// <summary>
        /// Gets or sets the name to filter modules by.
        /// </summary>
        public string ModuleNameFilter
        {
            get; set;
        }

        /// <summary>
        /// Gets or sets the selected module id.
        /// </summary>
        public int ModuleId
        {
            get
            {
                this.EnsureChildControls();

                int tabId;
                if (int.TryParse(this.hddSelector.SelectedValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out tabId))
                {
                    if (tabId <= 0 || String.IsNullOrEmpty(this.hddSelector.SelectedSlaveValue))
                    {
                        return -1;
                    }
                    int moduleId;
                    if (int.TryParse(this.hddSelector.SelectedSlaveValue, NumberStyles.Integer,
                                     CultureInfo.InvariantCulture, out moduleId))
                    {
                        return moduleId;
                    }
                }
                return -1;
            }
            set
            {
                this.EnsureChildControls();

                if (value > 0)
                {
                    ModuleInfo module = new ModuleController().GetModuleTabs(value).Cast<ModuleInfo>().FirstOrDefault();
                    this.hddSelector.SelectedValue = module.TabID.ToString(CultureInfo.InvariantCulture);
                    this.hddSelector.SelectedSlaveValue = value.ToString(CultureInfo.InvariantCulture);
                }
            }
        }

        private string cssClass;

        /// <summary>
        /// Gets or sets the Cascading Style Sheet (CSS) class rendered
        /// by the Web server control on the client.
        /// </summary>
        public string CssClass
        {
            get
            {
                return this.cssClass;
            }
            set
            {
                this.cssClass = value;

                if (this.ChildControlsCreated)
                {
                    this.hddSelector.CssClass = value;
                }
            }
        }

        private Unit width;

        /// <summary>
        /// Gets or sets the width of the Web server control.
        /// </summary>
        /// <returns>A <see cref="T:System.Web.UI.WebControls.Unit" /> that represents the width of the control. The default is <see cref="F:System.Web.UI.WebControls.Unit.Empty" />.</returns>
        /// <exception cref="T:System.ArgumentException">The width of the Web server control was set to a negative value.</exception>
        public Unit Width
        {
            get
            {
                return this.width;
            }
            set
            {
                this.width = value;

                if (this.ChildControlsCreated)
                {
                    this.hddSelector.Width = value;
                }
            }
        }

        private string validationGroup;

        /// <summary>
        /// Gets or sets the group of controls for which the control causes validation
        /// when it posts back to the server.
        /// </summary>
        public string ValidationGroup
        {
            get
            {
                return this.validationGroup;
            }
            set
            {
                this.validationGroup = value;

                if (this.ChildControlsCreated)
                {
                    this.hddSelector.ValidationGroup = value;
                }
            }
        }

        private string autoDetectText = "<Autodetect>";

        /// <summary>
        /// Gets or sets the text used for the "Autodetect" dropdown entry.
        /// </summary>
        public string AutoDetectText
        {
            get { return this.autoDetectText; }
            set { this.autoDetectText = value; }
        }

        private string noModuleAvailableText = "<no module available>";

        /// <summary>
        /// Gets or sets the text used for the "no module available" dropdown entry.
        /// </summary>
        public string NoModuleAvailableText
        {
            get { return this.noModuleAvailableText; }
            set { this.noModuleAvailableText = value; }
        }

        #endregion

        #region events

        /// <summary>
        /// Fires when the selected value has changed.
        /// </summary>
        public event EventHandler SelectedValueChanged;

        /// <summary>
        /// Fires the <see cref="SelectedValueChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="EventArgs"/> passed to the event.</param>
        protected void OnSelectedValueChanged(EventArgs e)
        {
            var handler = this.SelectedValueChanged;
            if (handler != null) 
                handler(this, e);
        }

        #endregion

        #region page lifecycle

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Init" /> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs" /> object that contains the event data.</param>
        protected override void OnInit(EventArgs e)
        {
            this.EnsureChildControls();

            base.OnInit(e);
        }

        /// <summary>
        /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            this.hddSelector = new HierarchicalDropDownList
                                   {
                                       CssClass = this.CssClass,
                                       Width = this.Width,
                                       ValidationGroup = this.ValidationGroup,
                                       DataTextField = "TabName",
                                       DataValueField = "TabID",
                                       SlaveDataTextField = "ModuleTitle",
                                       SlaveDataValueField = "ModuleID"
                                   };
            this.hddSelector.SlaveDataRequested += this.hddSelector_SlaveDataRequested;
            this.hddSelector.SelectedValueChanged += this.hddSelector_SelectedValueChanged;
            var tabs = Globals.GetPortalTabs(PortalController.GetCurrentPortalSettings().DesktopTabs, false, true, false, false).Cast<TabInfo>().Where(tab => PortalSecurity.IsInRoles(tab.AuthorizedRoles));
            this.hddSelector.DataSource = tabs;
            this.Controls.Add(this.hddSelector);

            this.DataBind();
            string autoDetect = String.IsNullOrEmpty(this.AutoDetectText) ? "<Autodetect>" : this.AutoDetectText;
            this.hddSelector.Items.Insert(0, new ListItem(autoDetect, "-1"));
        }

        #endregion

        #region event handlers

        void hddSelector_SlaveDataRequested(object sender, HierarchicalDataEventArgs e)
        {
            int tabId;
            if (int.TryParse(e.MasterValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out tabId))
            {
                if (tabId > 0)
                {
                    var moduleInfos =
                        new ModuleController().GetTabModules(tabId).AsEnumerable();
                    if (!string.IsNullOrEmpty(this.ModuleNameFilter))
                    {
                        moduleInfos = moduleInfos.Where(
                            m => m.Value.ModuleName.Equals(this.ModuleNameFilter));
                    }
                    var modules = moduleInfos.Select(m => m.Value).ToList();
                    if (modules.Count <= 0)
                    {
                        string noModuleAvailable = String.IsNullOrEmpty(this.NoModuleAvailableText) ? "<no module available>" : this.NoModuleAvailableText;
                        modules.Add(new ModuleInfo { ModuleID = -1, ModuleTitle = noModuleAvailable });
                    }
                    e.SlaveDataSource = modules;
                }
            }
        }

        void hddSelector_SelectedValueChanged(object sender, EventArgs e)
        {
            this.OnSelectedValueChanged(EventArgs.Empty);
        }

        #endregion
    }
}
